/**
 * Copyright (c) 2010-2011 Pierre Labatut
 *
 * This file is part of Space-Chronicles project.
 *
 * Space-Chronicles is free software: you can redistribute it and/or modify it
 * under the terms of its license which may be found in the accompanying
 * LICENSE file or <http://code.google.com/p/space-chronicles/>.
 */

package space.gl;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.media.opengl.GL;
import javax.media.opengl.GL3;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.KeyStroke;

/**
 *
 * @author Pierre
 */
public class FBODebugger extends javax.swing.JFrame {

    final AffineTransform transform = new AffineTransform();
    BufferedImage bi = null;
    float[] farray = null;

    public class ImagePanel extends JPanel {

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (bi != null) {
                final Graphics2D g2 = (Graphics2D) g.create();
                g2.transform(transform);
                g2.drawImage(bi, 0, 0, null);
                g2.drawLine(bi.getWidth()/2, 0, bi.getWidth()/2, bi.getHeight());
                g2.drawLine(0, bi.getHeight()/2, bi.getWidth(), bi.getHeight()/2);
            }
        }

        public ImagePanel() {
            final MouseAdapter ma = new MouseAdapter() {

                int pressX, pressY;
                AffineTransform pressT;

                @Override
                public void mousePressed(MouseEvent e) {
                    pressX = e.getX();
                    pressY = e.getY();
                    pressT = new AffineTransform(transform);

                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    int deltaX = e.getX() - pressX;
                    int deltaY = e.getY() - pressY;

                    transform.setToTranslation(deltaX, deltaY);
                    transform.concatenate(pressT);

                    repaint();
                }

                @Override
                public void mouseWheelMoved(MouseWheelEvent e) {
                    final double f = Math.pow(2, e.getWheelRotation());
                    AffineTransform at = new AffineTransform(transform);
                    transform.setToIdentity();
                    transform.translate(e.getX(), e.getY());
                    transform.scale(f, f);
                    transform.translate(-e.getX(), -e.getY());
                    transform.concatenate(at);

                    repaint();
                }

                @Override
                public void mouseMoved(MouseEvent e) {

                    if (bi == null) {
                        repaint();
                        return;
                    }
                    final Point2D imagePosition = new Point2D.Double();
                    try {
                        transform.inverseTransform(e.getPoint(), imagePosition);
                    } catch (NoninvertibleTransformException ex) {
                        throw new RuntimeException("The transformation should be inversible");
                    }
                    int x = (int) imagePosition.getX();
                    int y = (int) imagePosition.getY();
                    if (x < 0 || y < 0 || x >= bi.getWidth() || y >= bi.getHeight()) {
                        jLabel1.setText("");
                        return;
                    }
                    int rgb = bi.getRGB(x, y);
                    int b = (int) rgb & 255;
                    int g = (int) (rgb >>> 8) & 255;
                    int r = (int) (rgb >>> 16) & 255;
                    int a = (int) (rgb >>> 24) & 255;
                    final float l = farray[x + y * bi.getWidth()];
                    jLabel1.setText("x:" + x + " y:" + y + " rgb:" + r + "," + g + "," + b + " float:" + l);
                    repaint();

                }
            };
            addMouseWheelListener(ma);
            addMouseListener(ma);
            addMouseMotionListener(ma);
        }
    }

    void requestRefresh() {
        jLabel1.setText("Wait for refresh");
        bi = null;
        farray = null;
    }

    void notifyUpdate(final GL gl, final FrameBufferObject2x2 fbo) {
        // An invidible debuger means it is not active
        if (isVisible() == false) {
            return;
        }
        // The image is created from a frame buffer capture on demand.
        // A new image is demanded when bi is null
        if (bi != null) {
            return;
        }
        if (gl == null) {
            throw new IllegalArgumentException();
        }
        if (fbo == null) {
            throw new IllegalArgumentException();
        }


        {
            final ByteBuffer buffer = ByteBuffer.allocateDirect(Float.SIZE / Byte.SIZE * fbo.width * fbo.height);
            buffer.order(ByteOrder.nativeOrder());
            gl.glReadPixels(0, 0, fbo.width, fbo.height, GL3.GL_DEPTH_COMPONENT, GL3.GL_FLOAT, buffer);
            final FloatBuffer fbuffer = buffer.asFloatBuffer();
            farray = new float[fbuffer.capacity()];
            fbuffer.get(farray);
        }
        bi = new BufferedImage(fbo.width, fbo.height, BufferedImage.TYPE_BYTE_GRAY);
        int pos = 0;
        for (int y = 0; y < fbo.height; y++) {
            for (int x = 0; x < fbo.width; x++) {
                float f = farray[pos++];
                int v = (int) (f * 255.f);
                bi.setRGB(x, y, v + (v << 8) + (v << 16));
            }
        }

        setTitle(fbo.debugDescription(gl).replace("\n", " - "));
        repaint();
    }

    /** Creates new form FBODebugger */
    public FBODebugger() {
        initComponents();

        getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0), "REFRESH");
        getRootPane().getActionMap().put("REFRESH", new AbstractAction() {

            public void actionPerformed(ActionEvent e) {
                requestRefresh();
            }
        });



    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jPanel2 = new javax.swing.JPanel();
        jSeparator1 = new javax.swing.JSeparator();
        jLabel1 = new javax.swing.JLabel();
        jPanel1 = new ImagePanel();

        jPanel2.setLayout(new java.awt.BorderLayout());
        jPanel2.add(jSeparator1, java.awt.BorderLayout.NORTH);

        jLabel1.setText("jLabel1");
        jPanel2.add(jLabel1, java.awt.BorderLayout.CENTER);

        getContentPane().add(jPanel2, java.awt.BorderLayout.SOUTH);

        jPanel1.setCursor(null);
        getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER);
    }// </editor-fold>//GEN-END:initComponents
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel jLabel1;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JSeparator jSeparator1;
    // End of variables declaration//GEN-END:variables
}
